#include "stdafx.h"
#include "ConnectionSocketHandler.h"
#include "Logger.h"

#include "Poco/NObserver.h"
#include "Poco/Exception.h"
#include "Poco/Message.h"
#include "Poco/Logger.h"
#include "Poco/SAX/SAXParser.h"

namespace ExamSystem
{
    using Poco::NObserver;
    using Poco::Exception;
    using Poco::Logger;
    using Poco::Message;

    CConnectionSocketHandler::CConnectionSocketHandler(StreamSocket& socket, SocketReactor& reactor)
        : m_socket(socket)
        , m_reactor(reactor)
        , m_flagKeepAlivePackageReplied(true)
        , m_flagFirstLabelReceived(false)
    {
        m_reactor.addEventHandler(m_socket, NObserver<CConnectionSocketHandler, ReadableNotification>(*this, &CConnectionSocketHandler::OnReadable));
        m_reactor.addEventHandler(m_socket, NObserver<CConnectionSocketHandler, WritableNotification>(*this, &CConnectionSocketHandler::OnWritable));
        m_reactor.addEventHandler(m_socket, NObserver<CConnectionSocketHandler, ShutdownNotification>(*this, &CConnectionSocketHandler::OnShutdown));
        m_reactor.addEventHandler(m_socket, NObserver<CConnectionSocketHandler, ErrorNotification>(*this, &CConnectionSocketHandler::OnError));
        m_reactor.addEventHandler(m_socket, NObserver<CConnectionSocketHandler, TimeoutNotification>(*this, &CConnectionSocketHandler::OnTimeout));
    }

    CConnectionSocketHandler::~CConnectionSocketHandler(void)
    {
        m_reactor.removeEventHandler(m_socket, NObserver<CConnectionSocketHandler, ReadableNotification>(*this, &CConnectionSocketHandler::OnReadable));
        m_reactor.removeEventHandler(m_socket, NObserver<CConnectionSocketHandler, WritableNotification>(*this, &CConnectionSocketHandler::OnWritable));
        m_reactor.removeEventHandler(m_socket, NObserver<CConnectionSocketHandler, ShutdownNotification>(*this, &CConnectionSocketHandler::OnShutdown));
        m_reactor.removeEventHandler(m_socket, NObserver<CConnectionSocketHandler, ErrorNotification>(*this, &CConnectionSocketHandler::OnError));
        m_reactor.removeEventHandler(m_socket, NObserver<CConnectionSocketHandler, TimeoutNotification>(*this, &CConnectionSocketHandler::OnTimeout));
    }

    void CConnectionSocketHandler::SendKeepAlivePackage(void)
    {
        // we can't send new keep alive package until the last one gets reply
        if (!m_flagKeepAlivePackageReplied)
            return;

        // send the message
        const static char keepAlivePackage[] = "<package type=\"hold\"></package>";
        SendRawByte(keepAlivePackage, sizeof(keepAlivePackage));

        m_flagKeepAlivePackageReplied = false;
    }

    bool CConnectionSocketHandler::IsKeepAlivePackageReplied(void) const
    {
        return m_flagKeepAlivePackageReplied;
    }

    void CConnectionSocketHandler::OnReadable(const AutoPtr<ReadableNotification>& pNf)
    {
        try
        {
            int recvLength = m_socket.receiveBytes(m_receivingBuffer, ReceivingBufferSize - 1);

            if (recvLength)
            {
                m_receivingBuffer[recvLength] = '\0';
                m_receivedString += m_receivingBuffer;
                while (CheckReceivedString());
            }
        }
        catch (Exception& exc)
        {
            // net or i/o exception
            systemLogger.error(exc.name());
        }
    }

    void CConnectionSocketHandler::OnWritable(const AutoPtr<WritableNotification> &pNf)
    {

    }

    void CConnectionSocketHandler::OnShutdown(const AutoPtr<ShutdownNotification>& pNf)
    {
        systemLogger.information("Connection closed.");
        delete this;
    }

    void CConnectionSocketHandler::OnError(const AutoPtr<ErrorNotification>& pNf)
    {
        systemLogger.error("Connection error!");
        delete this;
    }

    void CConnectionSocketHandler::OnTimeout(const AutoPtr<TimeoutNotification>& pNf)
    {
        // do nothing now
        systemLogger.warning("Connection timeout.");
    }

    bool CConnectionSocketHandler::CheckReceivedString(void)
    {
        // we have processed last package, so all byte before next package should be ignored.
        size_t pos = m_receivedString.find_first_of('<');
        if (pos == m_receivedString.npos)
        {
            m_receivedString.clear();
            return false;
        }

        size_t pos2 = m_receivedString.find_first_of('>', pos);
        if (pos2 == m_receivedString.npos)   // we haven't get a complete package
            return false;

        static const char connectionStartLabel[] = "examination";
        if (!m_flagFirstLabelReceived)
        {
            if (pos2 - pos != sizeof(connectionStartLabel) || 
                m_receivedString.compare(pos, pos2 - pos - 1, connectionStartLabel))
            {
                // this is a invalid connection, close it
                m_socket.close();
                return false;
            }

            // this is a valid connection
            m_flagFirstLabelReceived = true;

            m_receivedString = m_receivedString.substr(pos2 + 1);
            return true;
        }

        // check if this is a complete stanza
        if (pos2 - pos != sizeof(connectionStartLabel) || 
            m_receivedString.compare(pos, pos2 - pos - 1, connectionStartLabel))
        {
            // this is a invalid connection, close it
            m_socket.close();
            return false;
        }

        std::string endLabel = "/" + m_receivedString.substr(pos + 1, pos2 - pos - 1);
        size_t endPos = m_receivedString.find(endLabel, pos2);

        return false;
    }

    void CConnectionSocketHandler::OnKeepAlivePackage(void)
    {
        // send reply package
        static const char keepAliveReply[] = "<package type=\"holdReplay\"></package>";
        SendRawByte(keepAliveReply, sizeof(keepAliveReply));
    }

    void CConnectionSocketHandler::OnKeepAliveReplyPackage(void)
    {
        m_flagKeepAlivePackageReplied = true;
    }

    bool CConnectionSocketHandler::SendRawByte(const void *data, int length)
    {
        if (data == NULL)
            return true;

        int sentLength = 0;

        try {
            while (sentLength < length)
            {
                int s = m_socket.sendBytes(reinterpret_cast<const char *>(data) + sentLength, length - sentLength);
                sentLength += s;
            }
        }
        catch (Exception& exc)
        {
            // net or i/o exception
            systemLogger.error(exc.name());
            return false;
        }

        return true;
    }

    bool CConnectionSocketHandler::SendXMLStanza(Document &xmlStanza)
    {
        return true;
    }

}